ECSとECRのコンテナ構成をCDKで実装してみた
こんにちは、つくぼし(tsukuboshi0755)です!
最近案件でCDK(TypeScript)を使うため、色んなAWSリソースをCDKで書けるように絶賛勉強中の身です。
今回は何番煎じになるか分からないですが、CDKv2を使って、ECSとECRのコンテナ構成を実装してみます!
前提条件
今回は以下の通り、CDKv2を使ってコードを書いていきます。
$ cdk version 2.83.1 (build 006b542)
またDockerクライアントとしては、Rancher Desktopを使用します。
$ rdctl version rdctl client version: 1.1.0, targeting server version: v1
全体構成
今回はVPC+ALB+ECS(Fargate)+ECRの構成を作成し、nginxコンテナをパブリック公開します。
なおDockerfileを用いてビルドしたイメージをECRにプッシュし、そのイメージを用いてECSタスクを起動する処理についても、全てCDKコードで自動化します。
リポジトリ
コード全体については、以下のリポジトリに格納していますのでご参照ください。
tsukuboshi/cdk-microservices-template
コード解説
CDKコードの中核となるlib/cdk-microservices-template-stack.ts
について説明します。
AWSアカウントID/リージョン/リソース名の設定
// Get AWS Account ID and Region const { accountId, region } = new ScopedAws(this); // Set Resource Name const resourceName = "test";
ScopedAws
を用いて、ECRリポジトリURLの設定に必要なAWSアカウントIDとリージョンを取得します。
またAWSリソース全体で使用するリソース名についても、ここで任意の名前を設定します。
ECR作成/イメージプッシュ実施
// Create ECR Repository const ecrRepository = new ecr.Repository(this, "EcrRepo", { repositoryName: `${resourceName}-ecr-repo`, removalPolicy: RemovalPolicy.DESTROY, autoDeleteImages: true, }); // Create Docker Image Asset const dockerImageAsset = new DockerImageAsset(this, "DockerImageAsset", { directory: path.join(__dirname, "..", "app"), platform: Platform.LINUX_AMD64, }); // Deploy Docker Image to ECR Repository new ecrdeploy.ECRDeployment(this, "DeployDockerImage", { src: new ecrdeploy.DockerImageName(dockerImageAsset.imageUri), dest: new ecrdeploy.DockerImageName( `${accountId}.dkr.ecr.${region}.amazonaws.com/${ecrRepository.repositoryName}:latest` ), });
今回のECRリポジトリは検証用のため、autoDeleteImages
をtrueで設定し、リポジトリにイメージが存在する場合でも削除できるようにしています。
またDockerImageAsset
を用いて、appディレクトリ内に存在するDockerfileを元に、イメージをビルドします。
さらにcdk-ecr-deployment
を用いて、ビルドしたイメージにlatastタグを付与してECRリポジトリにプッシュします。
なおcdk-ecr-deployment
はcdklabs
モジュールでの提供になるため、別途npmでインストールする必要がありますのでご注意ください。
- class DockerImageAsset (construct) · AWS CDK
- cdklabs/cdk-ecr-deployment: A CDK construct to deploy docker image to Amazon ECR
VPC作成
// Create VPC and Subnet const vpc = new ec2.Vpc(this, "Vpc", { vpcName: `${resourceName}-vpc`, maxAzs: 2, ipAddresses: ec2.IpAddresses.cidr("10.0.0.0/20"), subnetConfiguration: [ { cidrMask: 24, name: `${resourceName}-public`, subnetType: ec2.SubnetType.PUBLIC, }, ], });
ECSクラスターにVPCの指定が必要になるため、事前にVPCを作成します。
今回はVPC内にパブリックサブネットを2つ作成し、コンテナを作成したサブネット内で稼働させます。
ALB/ECS作成
// Create ECS Cluster const cluster = new ecs.Cluster(this, "EcsCluster", { clusterName: `${resourceName}-cluster`, vpc: vpc, }); // Create CloudWatch Log Group const logGroup = new logs.LogGroup(this, "LogGroup", { logGroupName: `/aws/ecs/${resourceName}`, removalPolicy: RemovalPolicy.DESTROY, }); // Create ALB and ECS Fargate Service const service = new ecs_patterns.ApplicationLoadBalancedFargateService( this, "FargateService", { loadBalancerName: `${resourceName}-lb`, publicLoadBalancer: true, cluster: cluster, serviceName: `${resourceName}-service`, cpu: 256, desiredCount: 2, memoryLimitMiB: 512, assignPublicIp: true, taskSubnets: { subnetType: ec2.SubnetType.PUBLIC }, taskImageOptions: { family: `${resourceName}-taskdef`, containerName: `${resourceName}-container`, image: ecs.ContainerImage.fromEcrRepository(ecrRepository, "latest"), logDriver: new ecs.AwsLogDriver({ streamPrefix: `container`, logGroup: logGroup, }), }, } );
事前にECSクラスターとロググループを作成した後、ecs_patterns.ApplicationLoadBalancedFargateService
モジュールを用いて、ALB/ECSサービス及び関連リソースをまとめて作成します。
今回はパブリックサブネット内にALB及びECSサービスを作成するため、publicLoadBalancer
とassignPublicIp
はtrueで設定します。
またtaskImageOption
のimage
にて、ECRリポジトリにプッシュしたlatestタグを持つイメージを指定し、タスクを起動させます。
さらにtaskImageOptions
のlogDriver
にて、タスクのログを事前に作成したロググループへ出力するように設定します。
動作確認
CDKコードのデプロイを行う事で、ECSタスクが正常に起動するか確認します。
初めに、必要なnpmパッケージのインストールとCDK実行環境の整備を事前に実施しておきます。
$ git clone https://github.com/tsukuboshi/cdk-microservices-template $ cd cdk-microservices-template $ npm install $ cdk bootstrap
またイメージビルドに必要なDockerクライアントを事前に起動させてください。
Rancher Desktopを使用している場合は、以下のコマンドで起動できます。
$ rdctl start
準備が整いましたら、以下の通りCDKコードをデプロイします。
変更セットを確認し、問題がなければyを入力します。
正常にデプロイが完了すると、アウトプットにALBのDNS名とECSサービスのURLが表示されます。
$ cdk deploy (中略) Do you wish to deploy these changes (y/n)? y (中略) Outputs: CdkMicroservicesTemplateStack.FargateServiceLoadBalancerDNSXXXXXXXX = <ALBのDNS名> CdkMicroservicesTemplateStack.FargateServiceServiceURLXXXXXXXX = <ECSサービスURL>
curlコマンドでALBのDNS名にアクセスすると、以下の通りnginxのデフォルトページが表示されました!
$ curl <ALBのDNS名> <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> html { color-scheme: light dark; } body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>
最後に
今回はCDKv2を使って、ECSとECRのコンテナ構成を実装してみました。
DockerImageAsset
モジュールとcdk-ecr-deployment
モジュールを組み合わせる事で、CDKコード内でイメージのビルドとECRへのプッシュが自動化できるのは非常に便利ですね。
またL2のaws_ecs_patterns
モジュールを使用する事で、ECSに必要なリソースを作成するためのコード記述量を減らせるので、ECS構成をCDKで書く場合は積極的に利用すると良さそうです。
ぜひCDKでECS構成を実装する際に、参考にしてみてください。
以上、つくぼし(tsukuboshi0755)でした!